﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using BPM.Common;
using BPM.Entity;
using Webdiyer.WebControls.Mvc;
using System.Data;

namespace BPM.Service
{
	public abstract class ServiceBase : ILazyProvider
	{
		protected BarcodeContext NewContext()
		{
			var context = new BarcodeContext();
			return context;
		}

		public abstract Result Delete(int id);

		protected abstract BaseEntity GetByIdCore(int id);

		BaseEntity ILazyProvider.GetById(int id)
		{
			return this.GetByIdCore(id);
		}
	}

	public abstract class ServiceBase<TEntity> : ServiceBase
		where TEntity : BaseEntity, new()
	{
		public Result Save(TEntity entity)
		{
			if (entity.Id > 0)
			{
				return Update(entity);
			}

			return Create(entity);
		}

		protected virtual Result Create(TEntity entity)
		{
			if (entity == null) throw new ArgumentNullException("entity");
			var res = entity.Validate();
			if (!res) return res;

			var context = this.NewContext();

			context.Set<TEntity>().Add(entity);

			res = context.Save().ReplaceMessage("添加成功。", "添加失败。");
			if (res) this.EnableLazyLoading(entity);
			return res;
		}

		/// <summary>
		/// 调用Save方法的时候，看来不能有任何关系。
		/// 如果外键还有外键，然后如果有循环的话，就Attach出错了。
		/// Attach是循环调用的。
		/// </summary>
		/// <param name="entity"></param>
		/// <returns></returns>
		protected Result Update(TEntity entity)
		{
			if (entity == null) throw new ArgumentNullException("entity");
			var res = entity.Validate();
			if (!res) return res;

			var context = this.NewContext();
			var set = context.Set<TEntity>();
			entity.EnableLazyLoading = false;
			set.Attach(entity);
			context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
			res = context.Save();
			entity.EnableLazyLoading = true;
			return res.ReplaceMessage("更新成功", "更新失败");
		}

		public override Result Delete(int id)
		{
			return this.Delete(id, null);
		}

		protected Result Delete(int id, Func<TEntity, Result> preDelete = null, Func<TEntity, Result> postDelete = null)
		{
			var context = this.NewContext();
			var set = context.Set<TEntity>();

			var entity = set.FirstOrDefault(e => e.Id == id);
			if (entity != null)
			{
				set.Remove(entity);

				if (preDelete != null)
				{
					var res = preDelete(entity);
					if (!res) return res;
				}

				var result = context.Save()
					.ReplaceMessage("删除成功", "数据被引用，删除失败，请先删除其相关联的数据。");
				if (!result) return result;

				if (postDelete != null)
				{
					var res = postDelete(entity);
					if (!res) return res;
				}

				return result;
			}

			return true;
		}

		public virtual TEntity GetById(int id)
		{
			if (id <= 0) return null;

			var result = this.Query().FirstOrDefault(e => e.Id == id);

			return this.EnableLazyLoading(result);
		}

		public virtual IList<TEntity> GetAll()
		{
			return this.EnableLazyLoading(this.Query().ToList());
		}

		public int Count()
		{
			return this.NewContext().Set<TEntity>().Count();
		}

		public virtual PagedList<TEntity> GetAll(int pageIndex, int pageSize)
		{
			var res = this.Query().ToPagedList(pageIndex, pageSize);
			this.EnableLazyLoading(res);
			return res;
		}

		protected IQueryable<TEntity> Query()
		{
			var set = this.NewContext().Set<TEntity>();
			return this.PrepareToQuery(set);
		}

		protected virtual IQueryable<TEntity> PrepareToQuery(DbSet<TEntity> set)
		{
			return set;
		}

		protected override BaseEntity GetByIdCore(int id)
		{
			return this.GetById(id);
		}

		/// <summary>
		/// 由于EF会主动访问外键，所以初始状态为不打开。
		/// 当要把实体返回出Service时，才使用这个方法把Lazy打开。
		/// </summary>
		/// <param name="entity"></param>
		/// <returns></returns>
		protected TEntity EnableLazyLoading(TEntity entity)
		{
			if (entity != null)
			{
				EnableLazyLoadingCore(entity);
			}
			return entity;
		}

		protected TCollection EnableLazyLoading<TCollection>(TCollection entities)
			where TCollection : IList<TEntity>
		{
			EnableLazyLoading(entities as IList<TEntity>);
			return entities;
		}

		protected IList<TEntity> EnableLazyLoading(IList<TEntity> entities)
		{
			for (int i = 0, c = entities.Count; i < c; i++)
			{
				var entity = entities[i];
				EnableLazyLoadingCore(entity);
			}

			return entities;
		}

		protected virtual void EnableLazyLoadingCore(TEntity entity)
		{
			entity.EnableLazyLoading = true;
		}

		protected static void EnableChildrenLazyLoading<T>(IList<T> entities)
			where T : BaseEntity
		{
			if (entities != null)
			{
				for (int i = 0, c = entities.Count; i < c; i++)
				{
					var entity = entities[i];
					entity.EnableLazyLoading = true;
				}
			}
		}
	}
}
